/*
 * Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
 *
 * Licensed under the Mulan PSL v2.
 * You can use this file according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of the License at
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
 * FOR MORE DETAILS.
 */

#pragma once

#include <algorithm>
#include <map>
#include <string>
#include <vector>

#include <highfive/H5Easy.hpp>

#include "Utility/Exception/SGException.h"

#include "DataStructure/Common/Out.h"

#include "DBManager/Database/HDF5Database.h"
#include "DBManager/IRepository/Base/IRepository.h"
#include "DBManager/Repository/HDF5/Base/DatasetInfo.h"
#include "DBManager/Repository/HDF5/Base/HDF5RepositoryMacro.h"

SG_DBMANAGER_HDF5_NAMESPACE_OPEN

// 浅尝模板元
// 检测是否有 m_sId 成员变量
template <typename T, typename = void>
struct has_sId : std::false_type
{};

template <typename T>
struct has_sId<T, std::void_t<decltype (std::declval<T> ().m_sId)>> : std::true_type
{};

// 检测是否有 m_id 成员变量
template <typename T, typename = void>
struct has_id : std::false_type
{};

template <typename T>
struct has_id<T, std::void_t<decltype (std::declval<T> ().m_id)>> : std::true_type
{};

// 检测是否有 m_eId 成员变量
template <typename T, typename = void>
struct has_eId : std::false_type
{};

template <typename T>
struct has_eId<T, std::void_t<decltype (std::declval<T> ().m_eId)>> : std::true_type
{};

/**
 * @brief 处理HDF5数据库的基础类，提供对数据表的操作接口。
 */
class DBManager_DECLSPEC RepositoryBase
{
  public:
    /**
     * @brief 枚举类型，用于标识不同的数据表。
     */
    enum DATASET : std::uint8_t
    {
        MainDataset,   ///< 数据所在表单
        IndexDataset,  ///< 所有ID表单
    };

    /**
     * @brief 纯虚函数，用于初始化表单信息，需由派生类实现。
     */
    virtual void InitialDatasetInfo () = 0;

    /**
     * @brief    在文件中创建表结构，如果在创建 Repository 时即创建表格
     * 将会导致出现很多空表，所以需要在合适的时机，进行表的创建
     *
     * @author   wuxingchuang
     * @date     2025.03.20
     */
    void CreateDatasetInFile ()
    {
        for (auto& iter : m_datasetInfo)
        {
            if (!IsExistData (iter.first))
            {
                iter.second.CreatTable (m_database.GetFile ());
                if (iter.first == MainDataset)
                {
                    auto dateset = m_database.GetFile ()->getDataSet (iter.second.GetPath ());
                    dateset.createAttribute<SG::DataStructure::Common::I64> ("version", 0);
                }
            }
        }
    }

    /**
     * @brief 初始化表单。如果表单不存在，则创建所有指定的表单，并更新表单的信息。
     *
     * 该函数执行以下操作：
     * 1. 检查主表（MainDataset）是否存在。如果不存在，则创建所有表单。
     * 2. 遍历表单信息，获取每个表单的数据集，并更新行数。
     * 3. 根据主表的数据大小计算最大缓存数量。
     *
     * 异常处理：
     * 如果在初始化过程中发生异常（例如文件操作失败），将捕获异常并输出错误信息。
     */
    virtual void InitDataset ()
    {
        try
        {
            if (m_datasetInfo.count (MainDataset) > 0)
            {
                auto& mainDatasetInfo = GetDatasetInfo (MainDataset);  ///< 获取主表的信息
                m_mainDatasetInfoPtr  = &mainDatasetInfo;

                // 遍历所有表单，获取数据集并更新行数
                if (m_database.GetFile ()->exist (mainDatasetInfo.GetPath ()))
                {
                    for (auto& iter : m_datasetInfo)
                    {
                        iter.second.SetRowCount (m_database.GetFile ()->getDataSet (iter.second.GetPath ()).getElementCount ());
                    }
                }
            }

            if (m_datasetInfo.count (IndexDataset) > 0)
            {
                m_indexDatasetInfoPtr = &m_datasetInfo[IndexDataset];
            }
        }
        catch (const std::exception& e)
        {
            throw SG::Utility::RuntimeError ("initalize dataset failed!");
        }
    }

    /**
     * @brief 构造函数，初始化RepositoryBase对象。
     *
     * @param database HDF5数据库的引用
     */
    explicit RepositoryBase (HDF5Database& database) : m_database (database)
    {}

    virtual ~RepositoryBase () = default;

    /**
     * @brief   : 注册添加数据表
     * @tparam D 数据类型
     * @param   : path 表路径
     * @param   : datasetKey
     * @author  : wxc
     * @date    : 2024.10.30
     */
    template <typename T>
    void RegisterTableInfo (const std::string& path, uint8_t datasetKey)
    {
        DatasetInfo info;
        info.SetDataSize (sizeof (T));
        info.SetPath (path);
        info.SetDataType (std::make_shared<HighFive::DataType> (HighFive::create_datatype<T> ()));
        m_datasetInfo[datasetKey] = info;
    }

    /**
     * @brief   : 注册添加数据表 ，并绑定类型
     * @param   : path
     * @param   : datasetKey
     * @param   : type
     */
    template <typename T>
    void RegisterIndex (const std::string& path, uint8_t datasetKey, T type)
    {
        RepositoryBase::RegisterTableInfo<SG::DataStructure::Common::Id> (path, datasetKey);

        if constexpr (std::is_same_v<T, SG::DataStructure::FEM::MaterialTypes>)
        {
            m_database.GetHDF5Hub()->m_materialTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::ElementTypes>)
        {
            m_database.GetHDF5Hub()->m_elementTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::CoordTypes>)
        {
            m_database.GetHDF5Hub()->m_coordinateTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::LoadType>)
        {
            m_database.GetHDF5Hub()->m_loadTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::ConstraintType>)
        {
            m_database.GetHDF5Hub()->m_constraintTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::RElementType>)
        {
            m_database.GetHDF5Hub()->m_rElementTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::DynamicType>)
        {
            m_database.GetHDF5Hub()->m_dynamicTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::PropertyTypes>)
        {
            m_database.GetHDF5Hub()->m_propertyTypeWithIndexPathMapping[type] = path;
        }
        else if constexpr (std::is_same_v<T, SG::DataStructure::FEM::TableTypes>)
        {
            m_database.GetHDF5Hub()->m_tableTypeWithIndexPathMapping[type] = path;
        }
    }

    /**
     * @brief 获取指定表单的表单信息，并更新行数。
     * @param tableKey 表单的键值
     * @return 表单信息
     * @throws std::runtime_error 如果未找到指定表单
     */
    DatasetInfo& GetDatasetInfo (uint8_t tableKey)
    {
        return m_datasetInfo[tableKey];
    };

    /**
     * @brief   :获取当前数据库文件的数据表
     * @param   : info
     * @author  : wxc
     * @date    : 2024.10.30
     */
    HighFive::DataSet GetDataset (DatasetInfo& info)
    {
        return m_database.GetFile ()->getDataSet (info.GetPath ());
    }

    /**
     * @brief   :获取当前数据库文件的数据表
     * @param   : tableKey
     * @author  : wxc
     * @date    : 2024.10.30
     */
    HighFive::DataSet GetDataset (uint8_t tableKey)
    {
        return m_database.GetFile ()->getDataSet (m_datasetInfo[tableKey].GetPath ());
    }

    /**
     * @brief   : 获取当前数据库文件的数据表
     * @param   : dataset_name
     * @author  : wxc
     * @date    : 2024.10.23
     */
    HighFive::DataSet GetDataset (const std::string& dataset_name)
    {
        return m_database.GetFile ()->getDataSet (dataset_name);
    }

    /**
     * @brief 判断是否存在指定表单。
     *
     * @return 如果存在返回true，否则返回false
     */
    bool IsExistData (uint8_t tableKey)
    {
        return m_database.GetFile ()->exist (m_datasetInfo[tableKey].GetPath ());
    }

    /**
     * @brief   : 写出数据至HDF5内
     * @tparam T 数据结构类型
     * @param   : datasetInfo 数据表信息
     * @param   : data 要写入的数据
     */
    template <typename T>
    bool WriteData (DatasetInfo& datasetInfo, std::vector<T>& data)
    {
        if (data.empty ())
            return true;

        // 如果还未创建表就去创建
        if (!datasetInfo.IsCreate ())
        {
            CreateDatasetInFile ();
        }

        // 获取数据集的路径和数据集对象
        HighFive::DataSet dataset = this->GetDataset (datasetInfo);

        // 获取数据集的大小和起始位置
        const std::size_t start = datasetInfo.GetRowCount ();
        const std::size_t count = data.size ();

        // 调整数据集的大小
        dataset.resize ({ start + count });

        // 写出数据
        dataset.select ({ start }, { count }).write (data);

        // 更新数据集的信息
        datasetInfo.SetRowCount (start + count);

        return true;
    }

    /**
     * @brief   : 写出数据至HDF5内
     * @tparam T 数据结构类型
     * @param   : tableKey 数据表key
     * @param   : data 要写入的数据
     */
    template <typename T>
    bool WriteData (uint8_t tableKey, std::vector<T>& data)
    {
        if (data.empty ())
            return true;

        return WriteData (GetDatasetInfo (tableKey), data);
    }

    /**
     * @brief   : 写出索引数据
     * @tparam T
     * @param   : datasetInfo
     * @param   : datas
     */
    template <typename T>
    void WriteIndexData (DatasetInfo& datasetInfo, std::vector<T>& datas)
    {
        std::vector<SG::DataStructure::Common::Id> idList;
        for (auto&& data : datas)
        {
            // 如果有sid 就是用sid
            if constexpr (has_sId<T>::value)
            {
                idList.emplace_back (data.m_sId);
            }
            // 如果有id 就是用id
            else if constexpr (has_id<T>::value)
            {
                idList.emplace_back (data.m_id);
            }
        }
        this->WriteData (datasetInfo, idList);
    }

    /// @brief 覆盖所有数据
    /// @tparam T
    /// @param datasetInfo
    /// @param data
    /// @return
    template <typename T>
    bool CoverData (DatasetInfo& datasetInfo, std::vector<T>& data)
    {
        if (data.empty ())
            return true;

        // 如果还未创建表就去创建
        if (!datasetInfo.IsCreate ())
        {
            CreateDatasetInFile ();
        }

        // 获取数据集的路径和数据集对象
        HighFive::DataSet dataset = this->GetDataset (datasetInfo);

        // 获取数据集的大小
        const std::size_t count = data.size ();

        // 调整数据集的大小
        dataset.resize ({ count });

        // 写出数据
        dataset.select ({ 0 }, { count }).write (data);

        // 更新数据集的信息
        datasetInfo.SetRowCount (count);

        return true;
    }

    /// @brief 覆盖部分数据
    /// @tparam T
    /// @param datasetInfo
    /// @param data
    /// @return
    template <typename T>
    bool CoverLocalData (DatasetInfo& datasetInfo, size_t offset, std::vector<T>& data)
    {
        if (data.empty ())
            return true;

        // 如果还未创建表就去创建
        if (!datasetInfo.IsCreate ())
        {
            CreateDatasetInFile ();
        }

        // 获取数据集的路径和数据集对象
        HighFive::DataSet dataset = this->GetDataset (datasetInfo);

        // 获取数据集的大小
        const std::size_t count = data.size ();

        // 调整数据集的大小
        dataset.resize ({ offset + count });

        // 写出数据
        dataset.select ({ offset }, { count }).write (data);

        // 更新数据集的信息
        datasetInfo.SetRowCount (count);

        return true;
    }

    /// @brief 覆盖数据
    /// @tparam T
    /// @param datasetInfo
    /// @param data
    /// @return
    template <typename T>
    bool CoverLocalData (DatasetInfo& datasetInfo, T& data)
    {
        // 如果还未创建表就去创建
        if (!datasetInfo.IsCreate ())
        {
            CreateDatasetInFile ();
        }

        // 获取数据集的路径和数据集对象
        HighFive::DataSet dataset = this->GetDataset (datasetInfo);

        // 调整数据集的大小
        dataset.resize ({ 1 });

        // 写出数据
        dataset.select ({ 0 }, { 1 }).write (data);

        // 更新数据集的信息
        datasetInfo.SetRowCount (1);

        return true;
    }

    std::map<uint8_t, DatasetInfo>& GetDatasetInfo ()
    {
        return m_datasetInfo;
    }
    void SetTableInfo (const std::map<uint8_t, DatasetInfo>& DatasetInfo)
    {
        m_datasetInfo = DatasetInfo;
    }

    SG::DBManager::HDF5Database& GetDatabase ()
    {
        return m_database;
    }

    DatasetInfo& GetMainDatasetInfo ()
    {
        return *m_mainDatasetInfoPtr;
    }

    DatasetInfo& GetIndexDatasetInfo ()
    {
        // 获取附表的信息;
        return *m_indexDatasetInfoPtr;
    }

  protected:
    // 成员变量
    SG::DBManager::HDF5Database&   m_database;     ///< 代表HDF5数据库的对象
    std::map<uint8_t, DatasetInfo> m_datasetInfo;  ///< 表单信息

    DatasetInfo* m_mainDatasetInfoPtr  = nullptr;  // 主表单指针
    DatasetInfo* m_indexDatasetInfoPtr = nullptr;  // 主表单指针
};

SG_DBMANAGER_HDF5_NAMESPACE_CLOSE
